-------------------------------------------------------------------------------
-- RTSAI - Real-Time Strategy engine for AI research
-- Copyright(C) 2007 DIMAp-UFRN, licensed under LGPL
--
-- RTSAI "Capture the flag" application
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- GLOBAL VARIABLES
--

NUM_UNITS = 4
FLAG_DIST = 20


function defPlayer ( player, side )
    for i = 1, NUM_UNITS do
        local state = player : get_name() .. "_starting"
        local unit = player : createUnit ( poti.base, state )
        local x = math.random ( -10, 10 )
        local z = side * (FLAG_DIST + math.random ( 5 ))
        unit : randomizeHeadingOnXZPlane ()
        unit : setPosition ( rtsai.Vector3D : new ( x, 0, z ) )
    end

    local flag_pos = rtsai.Vector3D : new ( 0, 0,  FLAG_DIST * side * (-1) )
    player.flag = game.objects.flag : new ( flag_pos )
    player.score = 0
    player.frozen = {}
end


--=============================================================================
-- GAME CONFIG
--=============================================================================

Game {
    -- PLAYERS ----------------------------------------------------------------
    players = {
        ab = { civilization = "poti", strategy = "ab_strategy" },
        cd = { civilization = "poti", strategy = "cd_strategy" }
    },

    -- ACTIONS ----------------------------------------------------------------
    -- the actions that unit may perform
    actions = {
        move = {
            execute = function ( self, unit, elapsed_time )
                unit:applySteeringForce ( self.steer, elapsed_time )
            end
        },

        change_state = {
            execute = function ( self, unit, elapsed_time )
                if self.state then
                    unit:change_state ( self.state )
                end
            end
        }
    },


    -- EVENTS -----------------------------------------------------------------
    -- events may happen during the game
    events = {
        flag_captured = function ()
            for _, p in pairs ( game.players ) do
                flag_pos = p.flag : position()
                for i = 0, p : get_num_units() - 1 do
                    local unit = p:get_unit( i )
                    local dist = rtsai.Vector3D:distance ( unit:position(), flag_pos )
                    if dist < unit:radius() + 1 then   -- 1 = flag radius
                        p.score = p.score + 1
                        return { player = p }  -- returning who captured the flag
                    end
                end
            end
        end,

        unit_frozen = function ()
            local next = pairs ( game.players )
            local _, p1 = next ( game.players )  -- first player in the table
            local p2 = p1.enemy                  -- its enemy

            local z_flag_p1 = p1.flag:position().z  -- 'z' to verify which terain

            for i = 0, p1:get_num_units() - 1 do
                local p1_unit = p1:get_unit( i )
                for j = 0, p2:get_num_units() - 1 do
                    local p2_unit = p2:get_unit( j )
                    local dist = rtsai.Vector3D:distance ( p1_unit:position(), p2_unit:position() )
                    if dist < p1_unit:radius() + p2_unit:radius() then
                        local z_p1_unit = p1_unit:position().z
                        if z_flag_p1 * z_p1_unit > 0 then
                            -- if the unit is is the same terrain of the flag, then it is in
                            -- an adversary terrain and it was captured
                            p1_unit:change_state ( frozen:new() )
                            p1.frozen [ p1_unit ] = true
                            return { player = p1, unit = p1_unit }
                        else
                            -- otherwise, it is in its own terrain and has captured p2_unit
                            p2_unit:change_state ( frozen:new() )
                            p2.frozen [ p2_unit ] = true
                            return { player = p2, unit = p2_unit }
                        end
                    end
                end
            end
        end,

        unit_wakeup = function ()
            for _, p in pairs ( game.players ) do
                for f, _ in pairs ( p.frozen ) do
                    for i = 0, p:get_num_units() - 1 do
                        local u = p:get_unit (i)
                        if not p.frozen [u] then
                            local dist = rtsai.Vector3D:distance ( f:position(), u:position() )
                            if dist < f:radius() + u:radius() then
                                p [ f ] = nil                     -- remove from the frozen list
                                return { player = p, unit = f }   -- return the event table
                            end
                        end
                    end
                end
            end
        end
    },


    -- OBJECTS ----------------------------------------------------------------
    -- the type of objects in the game. The objects themselves need to be
    -- created in the init function (according to the type defined here).
    objects = {
        flag = circ (1, false),  -- a circular object with radius 1. It is not an obstacle
    },

    -- AUXILIARY FUNCTIONS ----------------------------------------------------

    -- Game initialization.
    init = function ( self )
        local next = pairs ( game.players )    -- function to iterate over the players
        local k, p1 = next ( game.players )    -- first player in the table
        local _, p2 = next ( game.players, k ) -- second one
        
        defPlayer (p1, -1)
        defPlayer (p2,  1)

        p1.enemy = p2
        p2.enemy = p1
    end,

    loop_callback = function ( self )
        if game.current_time > 120 then  -- 2 minutes
            game:pause()
            print ("Sorry. Time's over.")
        end
    end
}


--=============================================================================
-- FROZEN STATE
--=============================================================================

State {
    name = "frozen",
    act = function ( self )
        -- do nothing
    end ,

    -- listen to the event when a unit is unfrozen
    on_unit_wakeup = function ( self, event )
        -- if the unfrozen unit is the state unit (itself), then change to wakingUp state
        if event.unit == self.unit then
            local state_str = self.unit:get_player() : get_name() .. "_wakingUp"
            self.unit:change_state ( _G[state_str] : new() )
        end
    end
}
